﻿using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using EnvDTE80;
using System.IO;

namespace CodeGen
{
    class CodeGenLogic
    {
        public CodeGenLogic(DTE2 applicationObject, AddIn addInInstance)
        {
            _applicationObject = applicationObject;
            _addInInstance = addInInstance;
        }

        public void CreateClass(List<String> namespaces, String className, String baseName)
        {
            if (className == "")
            {
                return;
            }

            String path = this.GetPath();
            if (path == "")
            {
                return;
            }

            this.CreateHFile(path, namespaces, className, baseName);
            this.CreateCppFile(path, namespaces, className);
            this.AddToProject(path, className);
        }

        private String GetPath()
        {
            String path = this.GetProjectPath();
            if (String.IsNullOrEmpty(path))
            {
                return "";
            }

            int index = path.LastIndexOf("\\");
            if (-1 == index)
            {
                index = path.LastIndexOf("/");
            }
            if (-1 == index)
            {
                return "";
            }

            return path.Substring(0, index + 1);
        }

        private String GetProjectPath()
        {
            SelectedItem item = _applicationObject.SelectedItems.Item(1);

            if (item.Project != null)
            {
                return item.Project.FileName;
            }

            if (item.ProjectItem != null)
            {
                return item.ProjectItem.ContainingProject.FileName;
            }

            return "";
        }

        private void CreateHFile(String filepath, List<String> namespaces, String className, String baseName)
        {
            bool hasBase = !String.IsNullOrEmpty(baseName);

            using (StreamWriter sw = File.CreateText(filepath + className + ".h"))
            {
                sw.WriteLine("#pragma once");
                sw.WriteLine("");

                if (hasBase)
                {
                    sw.WriteLine("#include \"{0}.h\"", baseName);
                    sw.WriteLine("");
                }

                int indent = 0;

                foreach (String ns in namespaces)
                {
                    Indent(sw, indent);
                    sw.WriteLine("namespace {0}", ns);
                    Indent(sw, indent);
                    sw.WriteLine("{");

                    ++indent;
                }

                Indent(sw, indent);
                if (hasBase)
                {
                    sw.WriteLine("class {0} : public {1}", className, baseName);
                }
                else
                {
                    sw.WriteLine("class {0}", className);
                }
                Indent(sw, indent);
                sw.WriteLine("{");
                Indent(sw, indent);
                sw.WriteLine("public:");
                ++indent;
                Indent(sw, indent);
                sw.WriteLine("{0}();", className);
                Indent(sw, indent);
                if (hasBase)
                {
                    sw.WriteLine("virtual ~{0}();", className);
                }
                else
                {
                    sw.WriteLine("~{0}();", className);
                }
                --indent;
                Indent(sw, indent);
                sw.WriteLine("};");

                foreach (String ns in namespaces)
                {
                    --indent;
                    Indent(sw, indent);
                    sw.WriteLine("}");
                }
            }
        }

        private void CreateCppFile(String filepath, List<String> namespaces, String className)
        {
            using (StreamWriter sw = File.CreateText(filepath + className + ".cpp"))
            {
                sw.WriteLine("#include \"stdafx.h\"");
                sw.WriteLine("#include \"{0}.h\"", className);
                sw.WriteLine("");

                sw.WriteLine("#ifdef _DEBUG");
                sw.WriteLine("#define new DEBUG_NEW");
                sw.WriteLine("#undef THIS_FILE");
                sw.WriteLine("static char THIS_FILE[] = __FILE__;");
                sw.WriteLine("#endif");
                sw.WriteLine("");

                int indent = 0;

                foreach (String ns in namespaces)
                {
                    Indent(sw, indent);
                    sw.WriteLine("namespace {0}", ns);
                    Indent(sw, indent);
                    sw.WriteLine("{");

                    ++indent;
                }

                Indent(sw, indent);
                sw.WriteLine("{0}::{1}()", className, className);
                Indent(sw, indent);
                sw.WriteLine("{");
                Indent(sw, indent);
                sw.WriteLine("}");

                sw.WriteLine("");

                Indent(sw, indent);
                sw.WriteLine("{0}::~{1}()", className, className);
                Indent(sw, indent);
                sw.WriteLine("{");
                Indent(sw, indent);
                sw.WriteLine("}");

                foreach (String ns in namespaces)
                {
                    --indent;
                    Indent(sw, indent);
                    sw.WriteLine("}");
                }
            }
        }

        private void Indent(StreamWriter sw, int indent)
        {
            for (int i = 0; i < indent; ++i)
            {
                sw.Write("\t");
            }
        }

        private void AddToProject(String path, String className)
        {
            ProjectItems items = this.GetProjectItems();
            if (items == null)
            {
                return;
            }

            items.AddFromFile(path + className + ".h");
            items.AddFromFile(path + className + ".cpp");
        }

        private ProjectItems GetProjectItems()
        {
            SelectedItem item = _applicationObject.SelectedItems.Item(1);

            if (item.Project != null)
            {
                return item.Project.ProjectItems;
            }

            if (item.ProjectItem != null)
            {
                return item.ProjectItem.ProjectItems;
            }

            return null;
        }

        private DTE2 _applicationObject;
        private AddIn _addInInstance;
    }
}
